package com.mvc.framework.dao;

import java.util.Date;
import java.util.Set;

import javax.persistence.Entity;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.ValidationException;
import javax.validation.Validator;

import org.apache.log4j.Logger;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.event.SaveOrUpdateEvent;
import org.hibernate.event.def.DefaultSaveOrUpdateEventListener;

import com.mvc.framework.config.ProjectConfig;
import com.mvc.framework.model.Auditable;
import com.mvc.framework.model.BaseEntity;
import com.mvc.framework.model.CommonAuditableEntity;
import com.mvc.framework.model.CompositeKeyAuditableEntity;
import com.mvc.framework.util.FrameworkContextUtils;

public class AuditListenerForSaveOrUpdate extends DefaultSaveOrUpdateEventListener {
    private static final long serialVersionUID = 349341376636499563L;
    private static final Logger LOGGER = Logger.getLogger(AuditListenerForSaveOrUpdate.class);
	private static final Validator VALIDATOR = Validation.buildDefaultValidatorFactory().getValidator();

	public void onSaveOrUpdate(SaveOrUpdateEvent event) {
		Object object = event.getObject();
		if (object instanceof CommonAuditableEntity) {
			CommonAuditableEntity entity = (CommonAuditableEntity) object;
			Integer userId = FrameworkContextUtils.getCurrentUserId();
			entity.setCreatedOn(new Date());
			entity.setCreatedBy(userId);
			entity.setRecStatus(CommonAuditableEntity.REC_STATUS_VALID);
		}else if(object instanceof CompositeKeyAuditableEntity){
			CompositeKeyAuditableEntity entity = (CompositeKeyAuditableEntity) object;
			Integer userId = FrameworkContextUtils.getCurrentUserId();
			entity.setCreatedOn(new Date());
			entity.setCreatedBy(userId);
			entity.setRecStatus(CommonAuditableEntity.REC_STATUS_VALID);
		}
		Set<ConstraintViolation<Object>> constraintViolations = VALIDATOR.validate(object);
		if(constraintViolations.size()>0){
			StringBuilder message = new StringBuilder(constraintViolations.size()*30);
			for(ConstraintViolation constraintViolation : constraintViolations){
				message.append(constraintViolation.getPropertyPath()).append(" ").append(constraintViolation.getMessage()).append(",");
			}
			throw new ValidationException(message.toString());
		}
		if (object instanceof CommonAuditableEntity && ((CommonAuditableEntity) object).getId()!=null) {
			Session session = event.getSession();
			saveHistory(object, (CommonAuditableEntity) object, session);
		}
		super.onSaveOrUpdate(event);
	}

	protected void saveHistory(Object object, BaseEntity entity, Session session) {
		if(ProjectConfig.isEnableHistory() && null != entity.getId()){
			StringBuilder hql = new StringBuilder(40);
	    	Entity entityClass = object.getClass().getAnnotation(Entity.class);
	    	String updateHql = "update " + entityClass.name() + " set updatedOn=?, updatedBy=?,recStatus=? where ID = " + entity.getId(); 
    		Query update = session.createQuery(updateHql.toString()).setParameter(0, new Date()).
    				setParameter(1, FrameworkContextUtils.getCurrentUserId()).setParameter(2, Auditable.REC_STATUS_INVALID);
    		update.executeUpdate();
	    	hql.append("INSERT INTO ").append(entityClass.name()).append(ProjectConfig.HISTORY_TABLE_SUFFIX).append(" SELECT * FROM ")
	    		.append(entityClass.name()).append(" WHERE ID = ").append(entity.getId());
	    	SQLQuery query = session.createSQLQuery(hql.toString());
	    	try{
	    		query.executeUpdate();
	    	}catch (HibernateException e) {
				LOGGER.error("saveHistory " + " EntityName: " + entityClass.name(), e);
	    	}
		}
    }

}
